package monitor

import (
	mylog "MyWebcrawler/log"
	"MyWebcrawler/scheduler"
	"context"
	"errors"
	"fmt"
	"github.com/sirupsen/logrus"
	"time"
)

var msgReachMaxIdleCount = "The scheduler has been idle for a period of time" + " (about %s)." + " Consider to stop it now."

var msgStopScheduler = "Stop scheduler...%s."

// Monitor 参数scheduler代表作为监控目标的调度器。
// 参数checkInterval代表检查间隔时间，单位：纳秒。
// 参数maxIdleCount代表最大空闲计数。
// 参数autoStop被用来指示该方法是否在调度器空闲足够长的时间之后自行停止调度器。
// 参数record代表日志记录函数。
// 当监控结束之后，该方法会向作为唯一结果值的通道发送一个代表了空闲状态检查次数的数值。
func Monitor(scheduler scheduler.Scheduler, checkInterval time.Duration, maxIdleCount uint, autoStop bool, record mylog.Record) <-chan uint64 {
	if scheduler == nil {
		panic(errors.New("The scheduler is invalid!"))
	}
	// 防止过小的检查间隔时间对爬取流程造成不良影响。
	if checkInterval < time.Millisecond*100 {
		checkInterval = time.Millisecond * 100
	}

	// 防止过小的最大空闲计数造成调度器的过早停止。
	if maxIdleCount < 15 {
		maxIdleCount = 15
	}
	logrus.Infof("Monitor parameters: checkInterval: %s,"+" maxIdleCount: %d, autoStop: %v", checkInterval, maxIdleCount, autoStop)

	stopNotifier, stopFunc := context.WithCancel(context.Background())

	reportError(scheduler, record, stopNotifier)
	// 检查计数通道
	checkCountChan := make(chan uint64, 2)
	// 检查空闲状态
	checkStatus(scheduler, checkInterval, maxIdleCount, autoStop, checkCountChan, record, stopFunc)
	return checkCountChan
}

func checkStatus(scheduler scheduler.Scheduler, checkInterval time.Duration, maxIdleCount uint, autoStop bool, checkCountChan chan<- uint64, record mylog.Record, stopFunc context.CancelFunc) {
	go func() {
		var checkCount uint64
		defer func() {
			stopFunc()
			checkCountChan <- checkCount
		}()
		waitForSchedulerStart(scheduler)
		var idleCount uint
		var firstIdleTime time.Time
		for {
			// 检查调度器的空闲状态。
			if scheduler.Idle() {
				idleCount++
				if idleCount == 1 {
					firstIdleTime = time.Now()
				}
				if idleCount >= maxIdleCount {
					msg := fmt.Sprintf(msgReachMaxIdleCount, time.Since(firstIdleTime).String())
					record(0, msg)
					if scheduler.Idle() {
						if autoStop {
							var result string
							if err := scheduler.Stop(); err == nil {
								result = "success"
							} else {
								result = fmt.Sprintf("failing(%s)", err)
							}
							msg = fmt.Sprintf(msgStopScheduler, result)
							record(0, msg)
						}
						break
					} else {
						if idleCount > 0 {
							idleCount = 0
						}
					}
				}
			} else {
				if idleCount > 0 {
					idleCount = 0
				}
			}
			checkCount++
			time.Sleep(checkInterval)
		}
	}()
}

func reportError(scheduler scheduler.Scheduler, record mylog.Record, stopNotifier context.Context) {
	go func() {
		waitForSchedulerStart(scheduler)
		errorChan := scheduler.ErrorChan()
		for {
			select {
			case <-stopNotifier.Done():
				return
			default:
			}
			err, ok := <-errorChan
			if ok {
				errMsg := fmt.Sprintf("Received an error from error channel: %s", err)
				record(2, errMsg)
			}
			time.Sleep(time.Microsecond)
		}
	}()
}

func waitForSchedulerStart(s scheduler.Scheduler) {
	for s.Status() != scheduler.SCHED_STATUS_STARTED {
		time.Sleep(time.Microsecond)
	}
}
